package roles

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"

	"xagent/internal/actor"
	proto "xagent/proto/v1"
)

// TestCase represents a test case
type TestCase struct {
	ID          string
	Name        string
	Description string
	Steps       []TestStep
	Priority    int
	Status      string
	CreatedAt   time.Time
	UpdatedAt   time.Time
	Results     []TestResult
	Metadata    map[string]interface{}
}

// TestStep represents a step in a test case
type TestStep struct {
	Order       int
	Description string
	Expected    string
	Actual      string
	Status      string
}

// TestResult represents the result of a test execution
type TestResult struct {
	ID        string
	TestID    string
	Status    string
	StartTime time.Time
	EndTime   time.Time
	Error     string
	Logs      []string
	Metrics   map[string]interface{}
}

// QAEngineer represents a QA engineer role
type QAEngineer struct {
	*actor.BaseActor
	testCases map[string]*TestCase
	results   map[string]*TestResult
	logger    *logrus.Logger
}

// NewQAEngineer creates a new QA engineer
func NewQAEngineer() *QAEngineer {
	baseActor := actor.NewBaseActor(
		"qa_engineer",
		"QAEngineer",
		"Responsible for quality assurance and testing",
	)

	qa := &QAEngineer{
		BaseActor: baseActor,
		testCases: make(map[string]*TestCase),
		results:   make(map[string]*TestResult),
		logger:    logrus.New(),
	}

	// Register actions
	qa.AddAction(&CreateTestCaseAction{qa})
	qa.AddAction(&ExecuteTestAction{qa})
	qa.AddAction(&AnalyzeTestResultsAction{qa})

	return qa
}

// Action implementations
type CreateTestCaseAction struct {
	qa *QAEngineer
}

func (a *CreateTestCaseAction) Name() string {
	return "create_test_case"
}

func (a *CreateTestCaseAction) Prerequisites() []string {
	return nil
}

func (a *CreateTestCaseAction) Run(ctx context.Context, input interface{}) (*proto.AgentMessage, error) {
	task, ok := input.(*proto.Task)
	if !ok {
		return nil, fmt.Errorf("invalid input type")
	}

	// Think about test case design
	thought, err := a.qa.Think(ctx, fmt.Sprintf("Design test cases for: %s", task.Description))
	if err != nil {
		return nil, err
	}

	// Create test case
	testCase := &TestCase{
		ID:          uuid.NewString(),
		Name:        task.Name,
		Description: thought.Content,
		Priority:    1,
		Status:      "DRAFT",
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
		Steps:       make([]TestStep, 0),
		Results:     make([]TestResult, 0),
		Metadata:    make(map[string]interface{}),
	}

	// Parse test steps from thought plan
	for i, step := range thought.Plan {
		testCase.Steps = append(testCase.Steps, TestStep{
			Order:       i + 1,
			Description: step,
			Status:      "PENDING",
		})
	}

	a.qa.testCases[testCase.ID] = testCase

	return &proto.AgentMessage{
		Id:   uuid.NewString(),
		Type: proto.MessageType_MESSAGE_TYPE_RESPONSE,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("Test case created: %s with %d steps", testCase.ID, len(testCase.Steps)),
		},
		Metadata: map[string]string{
			"test_case_id": testCase.ID,
			"status":       testCase.Status,
		},
	}, nil
}

type ExecuteTestAction struct {
	qa *QAEngineer
}

func (a *ExecuteTestAction) Name() string {
	return "execute_test"
}

func (a *ExecuteTestAction) Prerequisites() []string {
	return []string{"create_test_case"}
}

func (a *ExecuteTestAction) Run(ctx context.Context, input interface{}) (*proto.AgentMessage, error) {
	task, ok := input.(*proto.Task)
	if !ok {
		return nil, fmt.Errorf("invalid input type")
	}

	// Get test case ID from metadata
	testCaseID, ok := task.Metadata["test_case_id"]
	if !ok {
		return nil, fmt.Errorf("test case ID not provided")
	}

	testCase, ok := a.qa.testCases[testCaseID]
	if !ok {
		return nil, fmt.Errorf("test case not found: %s", testCaseID)
	}

	// Create new test result
	result := &TestResult{
		ID:        uuid.NewString(),
		TestID:    testCase.ID,
		Status:    "RUNNING",
		StartTime: time.Now(),
		Logs:      make([]string, 0),
		Metrics:   make(map[string]interface{}),
	}

	// Think about test execution
	thought, err := a.qa.Think(ctx, fmt.Sprintf("Execute test case: %s\nSteps: %v", testCase.Description, testCase.Steps))
	if err != nil {
		result.Status = "FAILED"
		result.Error = err.Error()
		return nil, err
	}

	// Update test steps with results
	success := true
	for i, step := range testCase.Steps {
		step.Status = "PASSED"
		step.Actual = fmt.Sprintf("Step %d executed successfully", i+1)

		// If the thought indicates any issues, mark step as failed
		if thought.Critical != "" && contains(thought.Critical, fmt.Sprintf("step %d", i+1)) {
			step.Status = "FAILED"
			step.Actual = thought.Critical
			success = false
		}

		result.Logs = append(result.Logs, fmt.Sprintf("Step %d: %s", i+1, step.Status))
	}

	result.EndTime = time.Now()
	result.Status = "PASSED"
	if !success {
		result.Status = "FAILED"
	}

	// Store result
	testCase.Results = append(testCase.Results, *result)
	a.qa.results[result.ID] = result

	return &proto.AgentMessage{
		Id:   uuid.NewString(),
		Type: proto.MessageType_MESSAGE_TYPE_RESPONSE,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("Test execution completed: %s, Status: %s", result.ID, result.Status),
		},
		Metadata: map[string]string{
			"test_case_id": testCase.ID,
			"result_id":    result.ID,
			"status":       result.Status,
		},
	}, nil
}

type AnalyzeTestResultsAction struct {
	qa *QAEngineer
}

func (a *AnalyzeTestResultsAction) Name() string {
	return "analyze_test_results"
}

func (a *AnalyzeTestResultsAction) Prerequisites() []string {
	return []string{"execute_test"}
}

func (a *AnalyzeTestResultsAction) Run(ctx context.Context, input interface{}) (*proto.AgentMessage, error) {
	task, ok := input.(*proto.Task)
	if !ok {
		return nil, fmt.Errorf("invalid input type")
	}

	// Think about test results analysis
	thought, err := a.qa.Think(ctx, fmt.Sprintf("Analyze test results for: %s", task.Description))
	if err != nil {
		return nil, err
	}

	// Calculate metrics
	metrics := make(map[string]interface{})
	totalTests := len(a.qa.testCases)
	passedTests := 0
	failedTests := 0
	var totalDuration time.Duration

	for _, result := range a.qa.results {
		if result.Status == "PASSED" {
			passedTests++
		} else {
			failedTests++
		}
		totalDuration += result.EndTime.Sub(result.StartTime)
	}

	metrics["total_tests"] = totalTests
	metrics["passed_tests"] = passedTests
	metrics["failed_tests"] = failedTests
	metrics["pass_rate"] = float64(passedTests) / float64(totalTests)
	metrics["average_duration"] = totalDuration / time.Duration(totalTests)

	return &proto.AgentMessage{
		Id:   uuid.NewString(),
		Type: proto.MessageType_MESSAGE_TYPE_RESPONSE,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("Test results analysis: %s\nMetrics: %v", thought.Content, metrics),
		},
		Metadata: map[string]string{
			"pass_rate": fmt.Sprintf("%.2f", metrics["pass_rate"]),
			"analysis":  thought.Reasoning,
		},
	}, nil
}

// Helper methods
func (qa *QAEngineer) GetTestCase(id string) *TestCase {
	return qa.testCases[id]
}

func (qa *QAEngineer) GetTestResult(id string) *TestResult {
	return qa.results[id]
}

func (qa *QAEngineer) GetTestMetrics() map[string]interface{} {
	metrics := make(map[string]interface{})
	totalTests := len(qa.testCases)
	passedTests := 0
	failedTests := 0

	for _, result := range qa.results {
		if result.Status == "PASSED" {
			passedTests++
		} else {
			failedTests++
		}
	}

	metrics["total_tests"] = totalTests
	metrics["passed_tests"] = passedTests
	metrics["failed_tests"] = failedTests
	metrics["pass_rate"] = float64(passedTests) / float64(totalTests)

	return metrics
}
